# -*- coding: utf-8 -*-
import cgi
import os
import logging
import string
from datetime import timedelta

from datetime import datetime
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from django.utils import simplejson as json
from store.testrpc import *
from store.utils import *

class Supplier(db.Model):
  name = db.StringProperty()
  postal_addr = db.PostalAddressProperty() #廠商地址
  contact_phone = db.StringListProperty() #廠商電話,可多個
  contact_email = db.StringListProperty() #廠商Email,可多個
  note = db.StringProperty(multiline=True)
  #auto update
  update_user = db.UserProperty(auto_current_user=True)
  update_date = db.DateTimeProperty(auto_now=True)
  create_user = db.UserProperty(auto_current_user_add=True)
  create_date = db.DateTimeProperty(auto_now_add=True)

class Product(db.Model):
  name = db.StringProperty() #產品名
  product_sn = db.StringProperty() #產品編號
  brand = db.StringProperty() #品牌
  note = db.StringProperty(multiline=True)
  purchase_price = db.FloatProperty() #購買價
  ref_price = db.FloatProperty() #參考價
  spec = db.StringListProperty() #規格
  tag = db.StringListProperty() #標籤
  supplier = db.ReferenceProperty(Supplier, required=True)
  stock_quantity = db.IntegerProperty() # 貨物存量
  safe_quantity = db.IntegerProperty() # 貨物存量
  #auto update
  update_user = db.UserProperty(auto_current_user=True)
  update_date = db.DateTimeProperty(auto_now=True)
  create_user = db.UserProperty(auto_current_user_add=True)
  create_date = db.DateTimeProperty(auto_now_add=True)

class BatchInventory(db.Model): #批次 進貨、出貨、盤點
  category= db.StringProperty() #分類:進貨、出貨、盤點
  note = db.StringProperty(multiline=True)
  total_value = db.FloatProperty() # 金額
  #auto update
  update_user = db.UserProperty(auto_current_user=True)
  update_date = db.DateTimeProperty(auto_now=True)
  create_user = db.UserProperty(auto_current_user_add=True)
  create_date = db.DateTimeProperty(auto_now_add=True)

class ProductInventory(db.Model):
  batch = db.ReferenceProperty(BatchInventory, required=True)
  product = db.ReferenceProperty(Product, required=True)
  category= db.StringProperty() #分類:進貨、出貨、盤點
  quantity = db.IntegerProperty() # 數量
  value = db.FloatProperty() # 金額

class BatchInvHdl(webapp.RequestHandler):
  def get(self):
    self.response.out.write("Access BatchInvHdl")
    self.error(403) # access denied
  def post(self):
    logging.info(self.request)
    #logging.info(self.request.arguments())
    oper = self.request.get('oper')

    if oper == 'edit' or oper == 'add':
      val = self.request.get('id')

      try:
        id = int(val)
        bat = BatchInventory.get_by_id(id)
      except ValueError:
        id = 0
        bat = BatchInventory()

      props = ['category','note']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(bat, prop, unicode(val))

      props = ['total_value']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(bat, prop, float(val))

      bat.put()
      """ isinstance(props['name'], db.StringProperty)"""
      result = batch_to_dict(bat)
      json.dump(result, self.response.out)
      return
    if oper == 'del':
      id = int(self.request.get('id'))
      bat= Supplier.get_by_id(id)
      bat.delete()
      return
    self.error(403) # access denied
    return

class ProductInvHdl(webapp.RequestHandler):
  def get(self):
    self.response.out.write("Access ProductInvHdl")
    self.error(403) # access denied
  def post(self):
    logging.info(self.request)
    #logging.info(self.request.arguments())
    oper = self.request.get('oper')

    if oper == 'edit' or oper == 'add':

      if oper == 'add' :
        bid = int(self.request.get('bid'))
        batch = BatchInventory.get_by_id(bid)
        if not batch:
          self.response.out.write("No such batch id %d" % (bid,))
          self.error(500) # access denied
          return
        pid = int(self.request.get('pid'))
        product = Product.get_by_id(pid)
        if not product:
          self.response.out.write("No such product id %d" % (pid,))
          self.error(500) # access denied
          return

      val = self.request.get('id')

      try:
        id = int(val)
        p = ProductInventory.get_by_id(id)
      except ValueError:
        id = 0
        p = ProductInventory(batch=batch, product=product)

      props = ['category']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, unicode(val))

      props = ['value']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, float(val))

      props = ['quantity']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, int(val))

      p.put()
      result = productinv_to_dict(p)

      json.dump(result, self.response.out, ensure_ascii=False)
      return
    if oper == 'del':
      id = int(self.request.get('id'))
      p = ProductInventory.get_by_id(id)
      p.delete()
      return
    self.error(403) # access denied
    return

class ProductHdl(webapp.RequestHandler):
  def get(self):
    self.response.out.write("Access ProductHdl")
    self.error(403) # access denied
  def post(self):
    #logging.info(self.request)
    #logging.info(self.request.arguments())
    oper = self.request.get('oper')

    if oper == 'edit' or oper == 'add':

      if oper == 'add' :
        sid = int(self.request.get('sid'))
        sup = Supplier.get_by_id(sid)
        if not sup:
          self.response.out.write("No such supplier id %d" % (sid,))
          self.error(500) # access denied
          return

      val = self.request.get('id')

      try:
        id = int(val)
        p = Product.get_by_id(id)
      except ValueError:
        id = 0
        p = Product(supplier=sup)

      props = ['name', 'product_sn', 'brand', 'note']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, unicode(val))

      props = ['spec', 'tag']
      for prop in props:
        val = self.request.get(prop)
        if val : 
          list_val = unicode(val).split(',', 10) #I think limit 10 tags is enough
          logging.info('prop %s val %s list %s', prop, val, string.join(list_val,','))
          setattr(p, prop, list_val)

      props = ['purchase_price', 'ref_price']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, float(val))

      props = ['stock_quantity', 'safe_quantity']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(p, prop, int(val))

      p.put()
      result = product_to_dict(p)

      json.dump(result, self.response.out, ensure_ascii=False)
      return
    if oper == 'del':
      id = int(self.request.get('id'))
      p = Product.get_by_id(id)
      p.delete()
      return
    self.error(403) # access denied
    return

class SupplierHdl(webapp.RequestHandler):
  def get(self):
    self.response.out.write("Access SupplierHdl")
    self.error(403) # access denied
  def post(self):
    #logging.info(self.request)
    #logging.info(self.request.arguments())
    oper = self.request.get('oper')

    if oper == 'edit' or oper == 'add':
      val = self.request.get('id')

      try:
        id = int(val)
        sup = Supplier.get_by_id(id)
      except ValueError:
        id = 0
        sup = Supplier()

      props = ['name', 'postal_addr', 'note']
      for prop in props:
        val = self.request.get(prop)
        if val : setattr(sup, prop, unicode(val))

      props = ['contact_email', 'contact_phone']
      for prop in props:
        val = self.request.get(prop)
        if val:
          list_val = val.split(',', 10) #I think limit 10 tags is enough
          setattr(sup, prop, list_val)

      sup.put()
      """ isinstance(props['name'], db.StringProperty)"""
      result = supplier_to_dict(sup)
      json.dump(result, self.response.out)
      return
    if oper == 'del':
      id = int(self.request.get('id'))
      sup = Supplier.get_by_id(id)
      sup.delete()
      return
    self.error(403) # access denied
    return


def to_bool(val):
  return val in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly']

class RPCHdl(webapp.RequestHandler):
  """ Allows the functions defined in the RPCMethods class to be RPCed."""

  def __init__(self):
    webapp.RequestHandler.__init__(self)
    self.methods = RPCMethods()
  def get(self):
    func = None
   
    action = self.request.get('q')
    if action:
      if action[0] == '_':
        self.error(403) # access denied
        return
      else:
        func = getattr(self.methods, action, None)
   
    if not func:
      self.error(404) # file not found
      return

    args = parseQueryArg(self.request)
    result = func(args, self.request)
    json.dump(result, self.response.out, ensure_ascii=False)

def parseQueryArg(request):
  args = {
    '_search' : False,
    'rows' : 10,
    'page' : 1,
    'sidx' : '__key__',
    'sord' : 'asc'}
  val = request.get('_search')
  if val :
    args['_search'] = to_bool(val)
  val = request.get('rows')
  if val :
    args['rows'] = int(val)
  val = request.get('page')
  if val :
    args['page'] = int(val)
  val = request.get('sidx')
  if val :
    args['sidx'] = val
  val = request.get('sord')
  if val :
    args['sord'] = val
  logging.debug(args)
  return args

def supplier_to_dict(p):
  item = {}
  item['id'] = p.key().id()
  item['name'] = p.name
  item['postal_addr'] = p.postal_addr
  item['contact_phone'] = string.join(p.contact_phone,',')
  item['contact_email'] = string.join(p.contact_email,',')
  item['note'] = p.note
  return item

def product_to_dict(p):
  result = {}
  result['id'] = p.key().id()

  props = ['name', 'product_sn', 'brand', 'note', 'purchase_price', 'ref_price', 'stock_quantity', 'safe_quantity']
    
  for prop in props:
    result[prop] = getattr(p, prop)

  result['spec'] = string.join(p.spec,',')
  result['tag'] = string.join(p.tag,',')
  if p.supplier:
    result['supplier'] = p.supplier.name
  return result;

searchOperDict= {'eq':' =', 'gt':' >', 'lt':' <', 'ge':' >=', 'le':' <='}

def batch_to_dict(p):
  result = {}
  result['id'] = p.key().id()
  props = ['category', 'note', 'total_value']
  for prop in props:
    result[prop] = getattr(p, prop)
  result['create_user'] = p.create_user.email()
  result['create_date'] =  (p.create_date + timedelta(hours=+8)).strftime("%Y-%m-%d %H:%M:%S")
  return result


def productinv_to_dict(p):
  result = {}
  result['id'] = p.key().id()
  result['bid'] = p.batch.key().id()
  result['pid'] = p.product.key().id()
  result['product'] = p.product.name
  props = ['category', 'quantity', 'value']
  for prop in props:
    result[prop] = getattr(p, prop)
  return result
class RPCMethods:
  """ Defines the methods that can be RPCed.
  NOTE: Do not allow remote callers access to private/protected "_*" methods.
  """

  def batchinv(self, args, request):
    data = {}
    query = BatchInventory.all()

    if args['sord'] == 'desc':
      query.order('-'+args['sidx'])
    else :
      query.order(args['sidx'])

    count = query.count() 
    # will have problem if entities larg then 1000, fix later
    rows = args['rows']
    total = int((count+rows-1)/rows)
    data['total'] = total

    page = args['page']
    if page < 0 or page > total:
      page = 1
    offset = (page - 1) * rows;

    data['page'] = page
    results = query.fetch(rows, offset=offset)
    data['records'] = len(results)
    data['rows'] = []
    for p in results:
      item = batch_to_dict(p)
      data['rows'] += [item]
    return data
  def productinv(self, args, request):
    data = {}
    query = ProductInventory.all()
    orderIsSet = False

    if args['_search'] :
      searchOper = request.get('searchOper')
      searchField = request.get('searchField')
      searchString = request.get('searchString')
      props = Product.properties()
      if searchOperDict.get(searchOper, False) and searchField and searchString :
        if searchField == '__key__':
          value = db.Key.from_path('ProductInventory', int(searchString))
        elif isinstance(props[searchField], db.FloatProperty) :
          value = float(searchString)
        elif isinstance(props[searchField], db.IntegerProperty) : 
          value = int(searchString)
        else :
          value = searchString
        query.filter(searchField + searchOperDict[searchOper], value)
        if searchOper != 'eq' :
          if args['sord'] == 'desc':
            query.order('-'+searchField)
          else:
            query.order(searchField)
          orderIsSet = True

      elif searchOper == 'bw' and searchField and searchString :
        if isinstance(props[searchField], db.StringProperty) :
          query.filter(searchField + ' >=', unicode(searchString))
          query.filter(searchField + ' <', unicode(searchString)+u'\ufffd')
          if args['sord'] == 'desc':
            query.order('-'+searchField)
          else:
            query.order(searchField)
          orderIsSet = True


    if not orderIsSet :
      if args['sord'] == 'desc':
        query.order('-'+args['sidx'])
      else :
        query.order(args['sidx'])


    val = request.get('bid')
    if val :
      query.filter('batch =', db.Key.from_path('BatchInventory', int(val)))
    count  = query.count() 
    rows = args['rows']
    total = int((count+rows-1)/rows)
    data['total'] = total

    page = args['page']
    if page < 0 or page > total:
      page = 1
    offset = (page - 1) * rows;

    data['page'] = page
    logging.info("fetch rows=%d, offset=%d" % (rows,offset,))
    results = query.fetch(rows, offset=offset)
    data['records'] = len(results)
    if len(results) > 0 :
      data['rows'] = []
      for p in results:
        item = productinv_to_dict(p)
        data['rows'] += [item]
        #logging.info("retreaving product tag %s" % (p.tag))
    return data
    return
  def supplier(self, args, request):
    data = {}
    query = Supplier.all()

    if args['sord'] == 'desc':
      query.order('-'+args['sidx'])
    else :
      query.order(args['sidx'])

    count = query.count() 
    # will have problem if entities larg then 1000, fix later
    rows = args['rows']
    total = int((count+rows-1)/rows)
    data['total'] = total

    page = args['page']
    if page < 0 or page > total:
      page = 1
    offset = (page - 1) * rows;

    data['page'] = page
    results = query.fetch(rows, offset=offset)
    data['records'] = len(results)
    data['rows'] = []
    for p in results:
      item = supplier_to_dict(p)
      data['rows'] += [item]
    return data
  def product(self, args, request):
    data = {}
    query = Product.all()
    orderIsSet = False

    if args['_search'] :
      searchOper = request.get('searchOper')
      searchField = request.get('searchField')
      searchString = request.get('searchString')
      props = Product.properties()
      if searchOperDict.get(searchOper, False) and searchField and searchString :
        if searchField == '__key__':
          value = db.Key.from_path('Product', int(searchString))
        elif isinstance(props[searchField], db.FloatProperty) :
          value = float(searchString)
        elif isinstance(props[searchField], db.IntegerProperty) : 
          value = int(searchString)
        else :
          value = searchString
        query.filter(searchField + searchOperDict[searchOper], value)
        if searchOper != 'eq' :
          if args['sord'] == 'desc':
            query.order('-'+searchField)
          else:
            query.order(searchField)
          orderIsSet = True

      elif searchOper == 'bw' and searchField and searchString :
        if isinstance(props[searchField], db.StringProperty) :
          query.filter(searchField + ' >=', unicode(searchString))
          query.filter(searchField + ' <', unicode(searchString)+u'\ufffd')
          if args['sord'] == 'desc':
            query.order('-'+searchField)
          else:
            query.order(searchField)
          orderIsSet = True


    if not orderIsSet :
      if args['sord'] == 'desc':
        query.order('-'+args['sidx'])
      else :
        query.order(args['sidx'])


    val = request.get('sid')
    if val :
      query.filter('supplier =', db.Key.from_path('Supplier', int(val)))
    count  = query.count() 
    rows = args['rows']
    total = int((count+rows-1)/rows)
    data['total'] = total

    page = args['page']
    if page < 0 or page > total:
      page = 1
    offset = (page - 1) * rows;

    data['page'] = page
    logging.info("fetch rows=%d, offset=%d" % (rows,offset,))
    results = query.fetch(rows, offset=offset)
    data['records'] = len(results)
    if len(results) > 0 :
      data['rows'] = []
      for p in results:
        item = product_to_dict(p)
        data['rows'] += [item]
        #logging.info("retreaving product tag %s" % (p.tag))
    return data

application = webapp.WSGIApplication(
                                     [
                                       ('/store/rpc', RPCHdl),
                                       ('/store/jsonmap', JsonMapHdl),
                                       ('/store/jsontest', JsonTestHdl),
                                       ('/store/product', ProductHdl),
                                       ('/store/batchinv', BatchInvHdl),
                                       ('/store/productinv', ProductInvHdl),
                                       ('/store/supplier', SupplierHdl)
                                     ],
                                     debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()
